import { ComponentType, ReactNode } from 'react';
import { createBrowserRouter, LoaderFunction, RouteObject } from 'react-router-dom';
import { ForbiddenException } from '@/service/http/errors/forbidden';

export type FCRouteObject = {
  key: string;
  menu?: { name: string; icon?: ReactNode };
  hideInMenu?: boolean;
  checkPermission?: (data?: any) => boolean;
  children?: FCRouteObject[];
} & Omit<RouteObject, 'children'>;

export type FormartToRouteObjectOptions = {
  initialLoader: (tstRoutes?: FCRouteObject[]) => Promise<any>;
};

export const formartToRouteObject = (
  tstRouteObejct: FCRouteObject,
  options?: FormartToRouteObjectOptions,
): RouteObject => {
  const { key, checkPermission, loader, Component, lazy } = tstRouteObejct;
  const initialLoader = options?.initialLoader || (() => Promise.resolve());

  let loaderProxy: LoaderFunction | null = null;

  const ComponentProxy: ComponentType | null | undefined = Component;

  let lazyProxy: any = lazy;

  if (key) {
    loaderProxy = async (args) => {
      return await initialLoader?.().then((data) => {
        if (checkPermission) {
          if (checkPermission(data)) {
            return (loader as LoaderFunction)?.(args) || null;
          } else {
            throw new ForbiddenException();
          }
        } else {
          return (loader as LoaderFunction)?.(args) || null;
        }
      });
    };

    if (checkPermission && lazy) {
      lazyProxy = async () => {
        return await initialLoader?.().then((data) => {
          if (checkPermission(data)) {
            return lazy?.();
          }
        });
      };
    }
  }

  return {
    ...tstRouteObejct,
    loader: loaderProxy,
    Component: ComponentProxy,
    lazy: lazyProxy,
  } as RouteObject;
};

export const formatToRoutes = (tstRoutes: FCRouteObject[], options?: FormartToRouteObjectOptions) => {
  return tstRoutes.map((route: FCRouteObject) => {
    const { children } = route;
    const newChildren: RouteObject[] = (children && formatToRoutes(children, options)) || [];

    return { ...formartToRouteObject(route, options), children: newChildren };
  }) as RouteObject[];
};

export const createFCBrowserRouter = (tstRoutes: FCRouteObject[], options?: FormartToRouteObjectOptions) => {
  if (options?.initialLoader) {
    options?.initialLoader(tstRoutes);
  }

  formatToRoutes(tstRoutes, options);

  return createBrowserRouter(formatToRoutes(tstRoutes, options));
};
